// Copyright 2008 Senary
// See credits.txt for details.

// This file is part of Omega VII.

// Omega VII is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Omega VII is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Omega VII.  If not, see <http://www.gnu.org/licenses/>.

#include <utility>

#include "SpaceObject.h"
#include "Constants.h"
#include "ImagePanel.h"
#include "UnitDecorator.h"

using namespace std;

SpaceObject::SpaceObject(void)
	:  _owner(0), _orbitalTarget(0), _orbitalTime((float)SHIP_ORBITING_TIME),
	_orbitalStartTime(0.0f),
	_imagePanel(0)
{
	// The angle should not be zero, as this means we face an undefined direction
	_angle = Vector(1.0, 0.0, 0.0);
}

SpaceObject::~SpaceObject(void)
{
	if (_imagePanel != 0) {
		delete _imagePanel;
	}
}

Vector SpaceObject::getPosition() const
{
	return _position;
}

Vector SpaceObject::getAngle() const
{
	return _angle;
}

Vector SpaceObject::getVelocity() const
{
	return _velocity;
}

Player * SpaceObject::getOwner() const
{
	return _owner;
}


void SpaceObject::setPosition(Vector const& position) 
{
	_position = position;
}

void SpaceObject::setAngle(Vector const& angle)
{
	_angle = angle;
}

void SpaceObject::setVelocity(Vector const& velocity) 
{
	_velocity = velocity;
}

void SpaceObject::setOwner(Player * owner)
{
	_owner = owner;
	
	// Update the Decoration Panel
	if (_imagePanel != 0) {
		delete _imagePanel;
	}
	_imagePanel = new ImagePanel(make_pair(0, 0), 2, UnitDecorator::getIcon(this));
	_imagePanel->createPanel();
	_imagePanel->setAlpha(30);
	_imagePanel->hide();
}

SpaceObject* SpaceObject::getOrbitalTarget(void)
{
	return _orbitalTarget;
}

void SpaceObject::setOrbitalTarget(SpaceObject* target)
{
	_orbitalTarget = target;
	if (_orbitalTarget == 0) {
		_orbitalEllipse = make_pair(Vector(), Vector());
	}
}

std::pair<Vector, Vector> SpaceObject::getOrbitalEllipse(void)
{
	return _orbitalEllipse;
}

void SpaceObject::setOrbitalEllipse(std::pair<Vector, Vector> const& ellipse)
{
	_orbitalEllipse = ellipse;
}

float SpaceObject::getOrbitalTime(void)
{
	return _orbitalTime;
}

void SpaceObject::setOrbitalTime(float orbitalTime)
{
	_orbitalTime = orbitalTime;
}

float SpaceObject::getOrbitalStartTime(void)
{
	return _orbitalStartTime;
}

void SpaceObject::setOrbitalStartTime(float orbitalStartTime)
{
	_orbitalStartTime = orbitalStartTime;
}


// Getter for the image panel
// Needed because it's updated through UnitDecorator
ImagePanel* SpaceObject::getDecorationPanel()
{
	return _imagePanel;
}


// starts orbiting around a planet... use this as an intelligent
// shortcut for the setOrbital* functions
// param totalTime: the current game time (to avoid jumps)
void SpaceObject::startOrbit(SpaceObject* target, float totalTime)
{
	setOrbitalTarget(target);

	Vector orbit1(_position - target->getPosition());
	Vector orbit2(orbit1);
	orbit2.rotate(Vector::randomVector(), M_PI / 2.0);
	setOrbitalEllipse(make_pair(orbit1, orbit2));
	setOrbitalStartTime(totalTime);
}

// Updated position, velocity etc. of the spaceship
// param timestep: time since the last update, in seconds
// param totalTime: total time the game has been running
void SpaceObject::update(float timestep, float totalTime) {
	// If an orbiting target is set, we let the object orbit around its target
	// For the moment this depends only on the absolute time...
	// for spaceships this may not be adequate because they can
	// start orbiting at any time (periods are not aligned with time 0)
	if (_orbitalTarget != 0) {
		Vector oldPos = _position;
		double phase = fmod(totalTime - _orbitalStartTime, _orbitalTime);
		phase = 2.0 * M_PI * phase / _orbitalTime;

		_position = _orbitalTarget->_position + 
			cos(phase) * _orbitalEllipse.first +
			sin(phase) * _orbitalEllipse.second;

		_velocity = (_position - oldPos) / timestep;
		_angle.rotateTowards(_velocity, SHIP_ROTATION_SPEED * timestep);
	}
	else {
		_position += _velocity * timestep;
	}
}

// This method is called when a spaceship died, so others can react
void SpaceObject::somebodyDied(SpaceObject const* who)
{
	if (_orbitalTarget == who) {
		setOrbitalTarget(0);
	}
}
